package com.ruoyi.common.utils;

import okhttp3.*;
import okio.Buffer;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * 用于简化 HTTP 请求的工具类，封装了 OkHttp 的基本用法。
 * 支持 GET 和 POST 请求，并且可以自定义超时设置，支持异步请求和请求头的设置。
 *
 * @author TsingYu
 * @version 1.0
 * @date 2024/11/12 14:50
 */
@SuppressWarnings("unused")
public class HttpUtils {
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);


    private Request request;

    private OkHttpClient client;

    /**
     * 获取请求后的信息
     *
     * @return 请求后的信息，只有在进行请求后才会有值返回，否则返回<code>null</code>
     */
    public Request getRequest() {
        return request;
    }

    /**
     * 构造一个 HttpUtils 实例，允许自定义超时设置。
     *
     * @param connectTimeout 连接超时时间（秒）
     * @param readTimeout    读取超时时间（秒）
     * @param writeTimeout   写入超时时间（秒）
     */
    public HttpUtils(long connectTimeout, long readTimeout, long writeTimeout) {
        this.client = new OkHttpClient.Builder()
                .addInterceptor(new LoggingInterceptor())
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .writeTimeout(writeTimeout, TimeUnit.SECONDS)
                .build();
    }

    /**
     * 用以下超时时间构造一个默认 HttpUtils 实例<br>
     * - 连接超时时间（60秒）<br>
     * - 读取超时时间（60秒）<br>
     * - 写入超时时间（60秒）
     */
    public HttpUtils() {
        this.client = new OkHttpClient.Builder()
                .addInterceptor(new LoggingInterceptor())
                .connectTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .build();
    }

    /**
     * 用以下超时时间构造一个默认 HttpUtils 实例，可选是否忽略ssl证书验证<br>
     * - 连接超时时间（60秒）<br>
     * - 读取超时时间（60秒）<br>
     * - 写入超时时间（60秒）<br>
     * 如果忽略证书失败，则创建默认实例
     *
     * @param ignoreSsl <code>true</code>：{@link #HttpUtils()}并忽略ssl证书验证；<br><code>false</code>：同 {@link #HttpUtils()}
     */
    public HttpUtils(boolean ignoreSsl) {
        this();
        if (ignoreSsl) {
            try {
                // 创建一个信任所有证书的 TrustManager
                TrustManager[] trustAllCerts = new TrustManager[]{
                        new X509TrustManager() {
                            @Override
                            public void checkClientTrusted(X509Certificate[] chain, String authType) {
                                // 不执行任何操作，信任所有客户端证书
                            }

                            @Override
                            public void checkServerTrusted(X509Certificate[] chain, String authType) {
                                // 不执行任何操作，信任所有服务器证书
                            }

                            @Override
                            public X509Certificate[] getAcceptedIssuers() {
                                return new X509Certificate[0];
                            }
                        }
                };
                // 创建 SSLContext
                SSLContext sslContext = SSLContext.getInstance("SSL");
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

                // 创建 OkHttpClient，配置忽略 SSL 验证
                this.client = this.client.newBuilder().sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                        .hostnameVerifier((hostname, session) -> true)
                        .build();
            } catch (NoSuchAlgorithmException | KeyManagementException e) {
                log.error("忽略ssl证书验证失败", e);
            }
        }
    }

    /**
     * 执行 GET 请求并返回响应内容。
     *
     * @param url 请求的 URL
     * @return 响应内容字符串
     * @throws IOException 如果请求失败或发生 I/O 错误
     */
    public String get(String url) throws IOException {
        System.out.println("============================== get   utl:" + url);
        Request request = new Request.Builder()
                .url(url)
                .build();
        return executeRequest(request);
    }

    /**
     * 执行带参数的 GET 请求。
     *
     * @param url    请求的 URL
     * @param params 请求参数
     * @return 响应内容字符串
     * @throws IOException 如果请求失败或发生 I/O 错误
     */
    public String get(String url, Map<String, String> params) throws IOException {
        HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
        }
        return get(urlBuilder.build().toString());
    }

    /**
     * 执行 POST 请求并返回响应内容。
     *
     * @param url  请求的 URL
     * @param json 请求的 JSON 字符串
     * @return 响应内容字符串
     * @throws IOException 如果请求失败或发生 I/O 错误
     */
    public String post(String url, String json) throws IOException {
        RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        return executeRequest(request);
    }

    /**
     * 执行带参数的 POST 请求。
     *
     * @param url    请求的 URL
     * @param params 请求参数
     * @return 响应内容字符串
     * @throws IOException 如果请求失败或发生 I/O 错误
     */
    public String post(String url, Map<String, String> params) throws IOException {
        FormBody.Builder formBuilder = new FormBody.Builder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            formBuilder.add(entry.getKey(), entry.getValue());
        }
        RequestBody formBody = formBuilder.build();
        Request request = new Request.Builder()
                .url(url)
                .post(formBody)
                .build();
        return executeRequest(request);
    }

    /**
     * 通用请求执行方法。
     *
     * @param request 要执行的请求
     * @return 响应内容字符串
     * @throws IOException 如果请求失败或发生 I/O 错误
     */
    private String executeRequest(Request request) throws IOException {
        this.request = request;
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response.code() + ": " + response.message() + ", response: " + (response.body() != null ? response.body().string() : null));
            }
            System.out.println("============================== executeRequest   utl:" + request.url());
            return response.body() != null ? response.body().string() : null;
        }
    }

    /**
     * 异步执行 GET 请求。
     *
     * @param url      请求的 URL
     * @param callback 请求回调接口
     */
    public void asyncGet(String url, Callback callback) {
        Request request = new Request.Builder()
                .url(url)
                .build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 异步执行 POST 请求。
     *
     * @param url      请求的 URL
     * @param json     请求的 JSON 字符串
     * @param callback 请求回调接口
     */
    public void asyncPost(String url, String json, Callback callback) {
        RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        client.newCall(request).enqueue(callback);
    }

    /**
     * 执行带请求头的 GET 请求。
     *
     * @param url     请求的 URL
     * @param headers 请求头
     * @return 响应内容字符串
     * @throws IOException 如果请求失败或发生 I/O 错误
     */
    public String getWithHeaders(String url, Map<String, String> headers) throws IOException {
        Request.Builder requestBuilder = new Request.Builder().url(url);
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            requestBuilder.addHeader(entry.getKey(), entry.getValue());
        }
        return executeRequest(requestBuilder.build());
    }

    /**
     * 执行带请求头的 POST 请求。
     *
     * @param url     请求的 URL
     * @param json    请求的 JSON 字符串
     * @param headers 请求头
     * @return 响应内容字符串
     * @throws IOException 如果请求失败或发生 I/O 错误
     */
    public String postWithHeaders(String url, String json, Map<String, String> headers) throws IOException {
        RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), json);
        Request.Builder requestBuilder = new Request.Builder().url(url).post(body);
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            requestBuilder.addHeader(entry.getKey(), entry.getValue());
        }
        return executeRequest(requestBuilder.build());
    }

    public static class LoggingInterceptor implements Interceptor {
        @NotNull
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            // 打印请求头
            Headers headers = request.headers();
            StringBuilder headerBuilder = new StringBuilder("[");
            for (String name : headers.names()) {
                headerBuilder.append(name).append(": ").append(headers.get(name)).append(",");
            }
            if (!headers.names().isEmpty()) {
                headerBuilder.deleteCharAt(headerBuilder.lastIndexOf(",")).append("]");
            } else {
                headerBuilder.append("]");
            }

            // 获取请求体并打印
            if (request.body() != null) {
                Buffer buffer = new Buffer();
                request.body().writeTo(buffer);
                log.error("开始发送Http请求，类型：{}，headers：{}，url：{}，body：{}", request.method(), headerBuilder, request.url(), buffer.readUtf8());
            } else {
                log.error("开始发送Http请求，类型：{}，headers：{}，url：{}", request.method(), headerBuilder, request.url());
            }

            // 继续处理请求
            return chain.proceed(request);
        }
    }

    public String getWithHeadersAndParams(String url, Map<String, String> params, Map<String, String> headers) throws IOException {
        // 构建带参数的URL
        HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder();
        if (params != null) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
            }
        }

        // 构建请求
        Request.Builder requestBuilder = new Request.Builder()
                .url(urlBuilder.build());

        // 添加请求头
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                requestBuilder.addHeader(entry.getKey(), entry.getValue());
            }
        }

        return executeRequest(requestBuilder.build());
    }
}
